using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
using System.Text;

namespace GetTogether.Web.Extension
{
    public class SoapLogs : SoapExtension
    {
        Stream _OldStream;
        Stream _NewStream;
        string LogsFolder = Utility.ConfigHelper.GetAppSetting("SoapLogs", "D:\\Logs\\Soap");
        const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
        const string LogSplit = "----------------------------------------------------------------------------------------------------------------------------------------------";
        public static string CurrentRequest;
        public static string CurrentResponse;
        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
        {
            return "";
            //return ((SoapTraceExtensionAttribute)attribute).Priority;
        }

        public override object GetInitializer(Type serviceType)
        {
            return serviceType.GetType();
        }

        public override void Initialize(object initializer)
        {

        }

        public string GetValueForSplit(string v, char separator)
        {
            return GetValueForSplit(v, separator, 0);
        }
        public string GetValueForSplit(string v, char separator, int lastIndex)
        {
            if (lastIndex == 0) lastIndex = 1;
            if (string.IsNullOrEmpty(v)) return v;
            string[] vi = v.Split('/');
            return vi[vi.Length - lastIndex];
        }
        private string GetMeghodName(SoapMessage msg)
        {
            string methodName = string.Empty;
            string action = "";
            try
            {
                action = msg.Action;
                if (!string.IsNullOrEmpty(action))
                {
                    methodName = GetValueForSplit(action, '/');
                }
            }
            catch //(Exception ex)
            {
                //Error(ex);
                if (System.Web.HttpContext.Current.Request != null && System.Web.HttpContext.Current.Request.Headers != null &&
                    System.Web.HttpContext.Current.Request.Headers.Count > 0)
                {
                    string soapAction = System.Web.HttpContext.Current.Request.Headers["SOAPAction"];
                    if (!string.IsNullOrEmpty(soapAction))
                    {
                        action = GetValueForSplit(soapAction, '/', 1).Replace("\"", "");
                        methodName = action;
                    }
                }
            }
            if (string.IsNullOrEmpty(methodName)) methodName = "No-Action";
            return methodName;
        }
        public override void ProcessMessage(SoapMessage msg)
        {
            string logFile = Path.Combine(LogsFolder, DateTime.Now.ToString("yyyy-MM-dd"));
            try
            {
                using (MemoryStream mStream = new MemoryStream())
                {
                    string logContent = string.Empty;
                    if (msg.Stage == SoapMessageStage.AfterSerialize)
                    {
                        Stream memStream;
                        if (_NewStream.CanSeek)
                        {
                            memStream = _NewStream;
                        }
                        else
                        {
                            memStream = new MemoryStream(((MemoryStream)_NewStream).ToArray());
                        }
                        memStream.Position = 0;
                        CopyStream(memStream, mStream);
                        memStream.Position = 0;
                        CopyStream(memStream, _OldStream);
                    }
                    else if (msg.Stage == SoapMessageStage.BeforeDeserialize)
                    {
                        CopyStream(_OldStream, _NewStream);
                        _NewStream.Position = 0;
                        CopyStream(_NewStream, mStream);
                        _NewStream.Position = 0;
                    }
                    //else
                    //{
                    //    CopyStream(_OldStream, _NewStream);
                    //    _NewStream.Position = 0;
                    //    CopyStream(_NewStream, mStream);
                    //    _NewStream.Position = 0;
                    //}
                    logContent = System.Text.Encoding.UTF8.GetString(mStream.ToArray());
                    if (!string.IsNullOrEmpty(logContent))
                    {
                        if (!string.IsNullOrEmpty(msg.Url))
                        {
                            logFile = Path.Combine(logFile, GetValueForSplit(msg.Url, '/').Split('.')[0]);
                        }
                        logFile = GetTogether.Utility.FileHelper.FixedInvalidChar4FilePath(logFile);
                        if (!System.IO.Directory.Exists(logFile)) System.IO.Directory.CreateDirectory(logFile);
                        string methodName = GetMeghodName(msg);
                        logFile = string.Concat(Path.Combine(logFile, methodName), DateTime.Now.ToString(" - HH"), ".txt");
                        logFile = GetTogether.Utility.FileHelper.FixedInvalidChar4FilePath(logFile);
                        StringBuilder sbLogs = new StringBuilder();
                        sbLogs.AppendLine("Soap Logging Start:");
                        DateTime dateTimeNow = DateTime.Now;
                        //sbLogs.AppendLine("Version:1.3.3");
                        sbLogs.AppendLine(string.Concat("Message In:", msg.Stage.ToString()));
                        sbLogs.AppendLine(string.Concat("Date Time:", dateTimeNow.ToString(DateTimeFormat)));
                        sbLogs.AppendLine(string.Concat("Action:", methodName));
                        sbLogs.AppendLine(string.Concat("URL:", msg.Url));
                        bool isResponse = logContent.IndexOf(string.Concat(methodName, "Response")) >= 0 ? true : false;
                        sbLogs.AppendLine(string.Concat("Action Type:", isResponse ? "Response" : "Request"));
                        if (!string.IsNullOrEmpty(logContent)) logContent = logContent.Trim(new char[] { '\r', '\n' });
                        if (isResponse)CurrentResponse = logContent;
                        else CurrentRequest = logContent;
                        using (FileStream fs = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
                        {
                            StreamWriter w = new StreamWriter(fs);
                            w.Write(sbLogs.ToString());
                            w.WriteLine("Soap Start:");
                            w.WriteLine(logContent);
                            w.WriteLine("Soap End");
                            w.WriteLine("Soap Logging End");
                            w.WriteLine(LogSplit);
                            w.Flush();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Error(ex);
            }
        }

        public void Error(Exception ex)
        {
            string logFile = Path.Combine(LogsFolder, DateTime.Now.ToString("yyyy-MM-dd"));
            if (!System.IO.Directory.Exists(logFile)) System.IO.Directory.CreateDirectory(logFile);
            string errorFile = Path.Combine(logFile, "Error.txt");
            //if (!System.IO.File.Exists(errorFile)) System.IO.File.Create(errorFile);
            using (FileStream fs = new FileStream(errorFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
            {
                StreamWriter w = new StreamWriter(fs);
                w.WriteLine(LogSplit);
                w.WriteLine(DateTime.Now);
                w.WriteLine(ex.ToString());
                w.Flush();
            }
        }
        public override Stream ChainStream(Stream stream)
        {
            _OldStream = stream;
            _NewStream = new MemoryStream();
            return _NewStream;
        }

        void CopyStream(Stream from, Stream to)
        {
            TextReader reader = new StreamReader(from);
            TextWriter writer = new StreamWriter(to);
            writer.WriteLine(reader.ReadToEnd());
            writer.Flush();
        }
    }

    [AttributeUsage(AttributeTargets.Method)]
    public class SoapLogsAttribute : SoapExtensionAttribute
    {
        private int _Priority;
        public override int Priority
        {
            get { return _Priority; }
            set { _Priority = value; }
        }

        public override Type ExtensionType
        {
            get { return typeof(SoapLogs); }
        }
    }
}
